﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace MaterialEditor
{
    public class ViewWindow : Panel
    {
        bool drawSelectorRect;
        System.Drawing.Rectangle selectorRect
        {
            get
            {

                int x = Math.Min(startDrawSelectorRectPoint.X, endDrawSelectorRectPoint.X);
                int y = Math.Min(startDrawSelectorRectPoint.Y, endDrawSelectorRectPoint.Y);
                int width = Math.Abs(startDrawSelectorRectPoint.X - endDrawSelectorRectPoint.X);
                int height = Math.Abs(startDrawSelectorRectPoint.Y - endDrawSelectorRectPoint.Y);

                return new System.Drawing.Rectangle(x, y, width, height);
            }
        }
        Point startDrawSelectorRectPoint;
        Point endDrawSelectorRectPoint;

        public List<Node> nodes = new List<Node>();
        public List<NodeConnector> nodeConnectors = new List<NodeConnector>();
        public List<NodeGroupBox> groupBoxes = new List<NodeGroupBox>();

        public IEnumerable<Node> pickupNodes
        {
            get
            {
                for (int i = 0; i < nodes.Count; i++)
                    if (nodes[i].Pickup)
                        yield return nodes[i];
            }
        }

        /// <summary>
        /// Node that has spawned the edit context menu
        /// </summary>
        public Node selectedNode;

        /// <summary>
        /// The selected node that's closest to the mouse cursor
        /// </summary>
        private Node MainSelectedNode
        {
            get
            {
                Node mainSelected = null;
                float minDistance = float.MaxValue;
                foreach (Node n in pickupNodes)
                {
                    minDistance = Math.Min(minDistance, distance(n.position, MousePosition));
                    if (minDistance == distance(n.position, MousePosition))
                        mainSelected = n;
                }


                return mainSelected;
            }
        }

        private new Microsoft.Xna.Framework.Vector2 MousePosition
        {
            get
            {
                Point c_pos = PointToClient(Cursor.Position);
                Microsoft.Xna.Framework.Vector2 mousePosition = new Microsoft.Xna.Framework.Vector2(c_pos.X, c_pos.Y);
                return Microsoft.Xna.Framework.Vector2.Transform(mousePosition, Microsoft.Xna.Framework.Matrix.Invert(Graphics.Instance.Camera));
            }
        }

        private NodeConnector ActiveConnector
        {
            get
            {
                for (int i = 0; i < nodeConnectors.Count; i++)
                    if (!nodeConnectors[i].placed)
                        return nodeConnectors[i];

                return null;
            }
        }


        public ViewWindow()
        {
            this.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

            DoubleBuffered = true;

            MouseDown += ViewWindow_MouseDown;
            MouseUp += ViewWindow_MouseUp;
            MouseMove += ViewWindow_MouseMove;
            MouseClick += ViewWindow_MouseClick;
            MouseDoubleClick += ViewWindow_MouseDoubleClick;
        }

        NodeGroupBox editBox;
        void ViewWindow_MouseDoubleClick(object sender, MouseEventArgs ee)
        {
            foreach (NodeGroupBox n in groupBoxes)
                if (n.TextBox.Contains(new Point((int)MousePosition.X, (int)MousePosition.Y)))
                {
                    editBox = n;
                    NameForm form = new NameForm(n.name);
                    form.FormClosing += form_FormClosing;
                    form.ShowDialog();

                    break;
                }
        }

        void form_FormClosing(object sender, FormClosingEventArgs e)
        {
            NameForm form = sender as NameForm;

            if (form.DialogResult == DialogResult.OK)
                editBox.name = form.textBox1.Text;
        }

        void ViewWindow_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (!drawSelectorRect)
                {
                    // find the main selected node (the one closest to the mouse) 
                    // and if it's hovering over a group box, add all selected nodes to that 
                    // group box
                    if (pickupNodes.Count() > 0) // ... if at least one node is selected of course
                    {

                        foreach (NodeGroupBox box in groupBoxes)
                        {
                            if (box.box.IntersectsWith(MainSelectedNode.Rect))
                            {
                                box.nodes.AddRange(pickupNodes);
                                break;
                            }
                        }
                    }

                    // deselect all selected nodes
                    bool foundSelectedNode = false;
                    //foreach (Node n in pickupNodes)
                    //    if (n.Rect.Contains(new Point((int)MousePosition.X, (int)MousePosition.Y)))
                    //    {
                    //        foundSelectedNode = true;
                    //        break;
                    //    }

                    if (!foundSelectedNode)
                    {
                        foreach (Node n in pickupNodes)
                            n.Pickup = false;
                    }
                }
                if (drawSelectorRect)
                {
                    drawSelectorRect = false;
                    startDrawSelectorRectPoint = endDrawSelectorRectPoint = Point.Empty;
                }

            }
            mb_prev = e.Button;
        }

        MouseButtons mb_prev;
        void ViewWindow_MouseDown(object sender, MouseEventArgs ee)
        {            
            if (ee.Button == System.Windows.Forms.MouseButtons.Left)
            {
                // attempt to connect nodes
                for (int i = 0; i < nodes.Count; i++)
                {
                    // if the user plugs a connector into a node
                    if (nodes[i].input != null && ActiveConnector == null)
                    {
                        foreach (InputSlot slot in nodes[i].input)
                        {
                            if (slot.Rect.Contains(new Point((int)MousePosition.X, (int)MousePosition.Y)))
                            {
                                if (slot.connector != null)
                                {
                                    slot.connector.placed = false;
                                    slot.connector = null;
                                }
                            }
                        }
                    }

                    // if the user clicks on the ouput of a node, begin connecting...
                    if (nodes[i].output != null && nodes[i].output.Rect.Contains(new Point((int)MousePosition.X, (int)MousePosition.Y)))
                    {
                        nodeConnectors.Add(new NodeConnector(nodes[i].output));
                    }
                }

                Node mouseOverNode = null;
                if (ActiveConnector == null)
                {
                    // find if the mouse is hovering over any nodes
                    foreach (Node n in nodes)
                        if (n.Rect.Contains(new Point((int)MousePosition.X, (int)MousePosition.Y)))
                        {
                            mouseOverNode = n;
                            break;
                        }
                    // if the mouse was not previously pressed, attempt to select any nodes the mouse is hovering over
                    if (mouseOverNode != null)
                        mouseOverNode.Pickup = true;
                }


                // prepare to begin drawing selector rect
                if (!drawSelectorRect && startDrawSelectorRectPoint == Point.Empty && mouseOverNode == null && ActiveConnector == null)
                {
                    startDrawSelectorRectPoint = new Point((int)MousePosition.X, (int)MousePosition.Y);
                }
                // begin drawing selector rect
                if (Math.Sqrt(Math.Pow(endDrawSelectorRectPoint.X - startDrawSelectorRectPoint.X, 2.0) +
                    Math.Pow(endDrawSelectorRectPoint.Y - startDrawSelectorRectPoint.Y, 2.0)) > 1.0 && mouseOverNode == null && ActiveConnector == null)
                {
                    foreach (Node n in pickupNodes)
                        n.Pickup = false;
                    drawSelectorRect = true;
                }
                if (drawSelectorRect)
                    endDrawSelectorRectPoint = new Point((int)MousePosition.X, (int)MousePosition.Y);
            }

            mousePrev = MousePosition;
            mb_prev = ee.Button;
        }

        const float snapDistance = 10.0f;

        void ViewWindow_MouseMove(object sender, MouseEventArgs ee)
        {
            if (ee.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (ActiveConnector != null)
                {
                    bool foundSnapNode = false;
                    foreach (Node n in nodes)
                    {
                        if (n.input != null)
                        {
                            foreach (InputSlot slot in n.input)
                            {
                                if (Math.Sqrt((Math.Pow(slot.position.X - MousePosition.X, 2)) +
                                    Math.Pow(slot.position.Y - MousePosition.Y, 2)) < snapDistance)
                                {
                                    ActiveConnector.endPoint_unplaced = slot.position;
                                    foundSnapNode = true;
                                }
                            }
                        }
                    }
                    if (!foundSnapNode)
                        ActiveConnector.endPoint_unplaced = new Microsoft.Xna.Framework.Vector2(MousePosition.X, MousePosition.Y);

                }
                if (!drawSelectorRect)
                    foreach (Node n in pickupNodes)
                        n.position += MousePosition - mousePrev;
                else
                    endDrawSelectorRectPoint = new Point((int)MousePosition.X, (int)MousePosition.Y);
                
            }
            mousePrev = MousePosition;
            mb_prev = ee.Button;
        }

        void ViewWindow_MouseUp(object sender, MouseEventArgs ee)
        {
            drawSelectorRect = false;
            startDrawSelectorRectPoint = Point.Empty;

            if (ActiveConnector != null)
            {
                bool foundSnapNode = false;

                foreach (Node n in nodes)
                {
                    if (n.input != null)
                    {
                        foreach (InputSlot slot in n.input)
                        {
                            if (slot.connector != null)
                                continue;
                            if (Math.Sqrt((Math.Pow(slot.position.X - MousePosition.X, 2)) +
                                Math.Pow(slot.position.Y - MousePosition.Y, 2)) < snapDistance)
                            {
                                ActiveConnector.endSlot = slot;
                                ActiveConnector.End = n;
                                ActiveConnector.startSlot.connector = ActiveConnector;
                                ActiveConnector.endSlot.connector = ActiveConnector;
                                foundSnapNode = true;
                                ActiveConnector.placed = true;
                                break;
                            }
                        }
                    }
                    if (foundSnapNode)
                        break;
                }

                if (!foundSnapNode)
                    nodeConnectors.Remove(ActiveConnector);
            }



            mousePrev = new Microsoft.Xna.Framework.Vector2(MousePosition.X, MousePosition.Y);
            mb_prev = ee.Button;
        }

        Microsoft.Xna.Framework.Vector2 mousePrev;

        public new void Update()
        {
            if (ActiveConnector == null)
            {
                for (int i = 0; i < nodes.Count; i++)
                {
                    nodes[i].Update();
                    if (nodes[i].Rect.IntersectsWith(selectorRect))
                        nodes[i].Pickup = true;
                }
            }
            // check if connectors have lost connection
            for (int i = 0; i < nodeConnectors.Count; i++)
            {
                if (nodeConnectors[i].placed && (nodeConnectors[i].Start == null || nodeConnectors[i].End == null))
                {
                    nodeConnectors.RemoveAt(i);
                    i--;
                }
            }

            // reset each groupbox to gray
            foreach (NodeGroupBox g in groupBoxes)
                g.borderColor = Color.Gray;
            // if the main selected node is intersecting with a group box, turn the boxes border color gray
            if (pickupNodes.Count() > 0)
            {
                foreach (NodeGroupBox gBox in groupBoxes)
                {
                    Node main = MainSelectedNode as Node;
                    if (main != null && !gBox.nodes.Contains(main))
                    {
                        Rectangle main_rect = main.Rect;
                        Rectangle gBox_rect = gBox.box;
                        if (main_rect.IntersectsWith(gBox_rect))
                            gBox.borderColor = Color.White;
                    }
                }
            }
        }

        private float distance(Microsoft.Xna.Framework.Vector2 a, Microsoft.Xna.Framework.Vector2 b)
        {
            return Microsoft.Xna.Framework.Vector2.Distance(a, b);
        }

        public void Draw()
        {
            for (int i = 0; i < nodes.Count; i++)
                nodes[i].Draw();
            for (int i = 0; i < nodeConnectors.Count; i++)
                nodeConnectors[i].Draw();
            for (int i = 0; i < groupBoxes.Count; i++)
                groupBoxes[i].Draw();

            if (drawSelectorRect)
                Graphics.Instance.DrawHollowRect(selectorRect, Color.DarkBlue);

        }

    }
}
